"""
题库管理模块 - 测试用例
"""
from django.test import TestCase, TransactionTestCase
from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import SimpleUploadedFile
from rest_framework.test import APITestCase
from rest_framework import status
from django.urls import reverse
import json
import io
import pandas as pd

from .models import (
    Question, QuestionBank, QuestionBankItem, QuestionTag,
    QuestionTagRelation, QuestionUsageHistory
)
from .utils import QuestionImporter, QuestionExporter, QuestionValidator

User = get_user_model()


class QuestionModelTests(TestCase):
    """题目模型测试"""

    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123',
            role='teacher'
        )

    def test_create_question(self):
        """测试创建题目"""
        question = Question.objects.create(
            title='测试题目',
            stem='这是一个测试题目',
            type='choice',
            options=['选项A', '选项B', '选项C', '选项D'],
            answer='A',
            creator=self.user,
            subject='数学',
            chapter='代数'
        )

        self.assertEqual(question.title, '测试题目')
        self.assertEqual(question.type, 'choice')
        self.assertEqual(question.creator, self.user)
        self.assertEqual(question.status, 'draft')
        self.assertEqual(question.difficulty, 3)  # 默认中等难度

    def test_formatted_options(self):
        """测试格式化选项"""
        question = Question.objects.create(
            title='选择题',
            stem='测试选择题',
            type='choice',
            options=['选项A', '选项B', '选项C', '选项D'],
            answer='A',
            creator=self.user
        )

        formatted = question.get_formatted_options()
        expected = {'A': '选项A', 'B': '选项B', 'C': '选项C', 'D': '选项D'}
        self.assertEqual(formatted, expected)

    def test_update_usage_statistics(self):
        """测试更新使用统计"""
        question = Question.objects.create(
            title='统计测试题',
            stem='测试统计',
            type='choice',
            options=['A', 'B'],
            answer='A',
            creator=self.user
        )

        # 初始状态
        self.assertEqual(question.usage_count, 0)
        self.assertIsNone(question.correct_rate)

        # 答对一次
        question.update_usage_statistics(is_correct=True)
        self.assertEqual(question.usage_count, 1)
        self.assertEqual(question.correct_rate, 1.0)

        # 答错一次
        question.update_usage_statistics(is_correct=False)
        self.assertEqual(question.usage_count, 2)
        self.assertEqual(question.correct_rate, 0.5)

    def test_can_access(self):
        """测试访问权限"""
        other_user = User.objects.create_user(
            username='otheruser',
            email='other@example.com',
            password='otherpass123',
            role='student'
        )

        # 私有题目
        private_question = Question.objects.create(
            title='私有题目',
            stem='测试私有题目',
            type='choice',
            options=['A', 'B'],
            answer='A',
            creator=self.user,
            is_public=False
        )

        self.assertTrue(private_question.can_access(self.user))  # 创建者可以访问
        self.assertFalse(private_question.can_access(other_user))  # 其他人不能访问

        # 公开题目
        public_question = Question.objects.create(
            title='公开题目',
            stem='测试公开题目',
            type='choice',
            options=['A', 'B'],
            answer='A',
            creator=self.user,
            is_public=True
        )

        self.assertTrue(public_question.can_access(other_user))  # 所有人都可以访问


class QuestionBankModelTests(TestCase):
    """题库模型测试"""

    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123',
            role='teacher'
        )
        self.question_bank = QuestionBank.objects.create(
            name='测试题库',
            description='这是一个测试题库',
            type='personal',
            subject='数学',
            owner=self.user
        )

    def test_create_question_bank(self):
        """测试创建题库"""
        self.assertEqual(self.question_bank.name, '测试题库')
        self.assertEqual(self.question_bank.owner, self.user)
        self.assertEqual(self.question_bank.question_count, 0)
        self.assertEqual(self.question_bank.total_points, 0)

    def test_update_statistics(self):
        """测试更新统计信息"""
        # 创建题目
        question1 = Question.objects.create(
            title='题目1',
            stem='测试题目1',
            type='choice',
            options=['A', 'B'],
            answer='A',
            creator=self.user,
            points=2.0
        )

        question2 = Question.objects.create(
            title='题目2',
            stem='测试题目2',
            type='choice',
            options=['A', 'B'],
            answer='A',
            creator=self.user,
            points=3.0
        )

        # 添加题目到题库
        QuestionBankItem.objects.create(
            question_bank=self.question_bank,
            question=question1
        )
        QuestionBankItem.objects.create(
            question_bank=self.question_bank,
            question=question2
        )

        # 更新统计信息
        self.question_bank.update_statistics()

        self.assertEqual(self.question_bank.question_count, 2)
        self.assertEqual(float(self.question_bank.total_points), 5.0)

    def test_can_access(self):
        """测试访问权限"""
        other_user = User.objects.create_user(
            username='otheruser',
            email='other@example.com',
            password='otherpass123',
            role='student'
        )

        # 私有题库
        private_bank = QuestionBank.objects.create(
            name='私有题库',
            type='personal',
            subject='英语',
            owner=self.user,
            is_public=False
        )

        self.assertTrue(private_bank.can_access(self.user))  # 所有者可以访问
        self.assertFalse(private_bank.can_access(other_user))  # 其他人不能访问

        # 公开题库
        public_bank = QuestionBank.objects.create(
            name='公开题库',
            type='school',
            subject='物理',
            owner=self.user,
            is_public=True
        )

        self.assertTrue(public_bank.can_access(other_user))  # 所有人都可以访问


class QuestionBankAPITests(APITestCase):
    """题库API测试"""

    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123',
            role='teacher'
        )
        self.client.force_authenticate(user=self.user)

        self.question_bank = QuestionBank.objects.create(
            name='API测试题库',
            type='personal',
            subject='化学',
            owner=self.user
        )

    def test_list_question_banks(self):
        """测试获取题库列表"""
        url = reverse('question-bank:questionbank-list')
        response = self.client.get(url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data['results']), 1)

    def test_create_question_bank(self):
        """测试创建题库"""
        url = reverse('question-bank:questionbank-list')
        data = {
            'name': '新题库',
            'description': '新的测试题库',
            'type': 'personal',
            'subject': '生物',
            'grade_level': '高中',
            'difficulty_range': [2, 4]
        }
        response = self.client.post(url, data)

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(QuestionBank.objects.count(), 2)

    def test_retrieve_question_bank(self):
        """测试获取题库详情"""
        url = reverse('question-bank:questionbank-detail', args=[self.question_bank.id])
        response = self.client.get(url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['name'], 'API测试题库')

    def test_update_question_bank(self):
        """测试更新题库"""
        url = reverse('question-bank:questionbank-detail', args=[self.question_bank.id])
        data = {
            'name': '更新后的题库名称',
            'description': '更新后的描述'
        }
        response = self.client.patch(url, data)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.question_bank.refresh_from_db()
        self.assertEqual(self.question_bank.name, '更新后的题库名称')

    def test_delete_question_bank(self):
        """测试删除题库"""
        url = reverse('question-bank:questionbank-detail', args=[self.question_bank.id])
        response = self.client.delete(url)

        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertEqual(QuestionBank.objects.count(), 0)


class QuestionImporterTests(TransactionTestCase):
    """题目导入器测试"""

    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123',
            role='teacher'
        )
        self.question_bank = QuestionBank.objects.create(
            name='导入测试题库',
            type='personal',
            subject='测试',
            owner=self.user
        )

    def test_import_questions_from_excel(self):
        """测试从Excel导入题目"""
        # 创建测试数据
        data = {
            '题目类型': ['单选题', '多选题', '判断题'],
            '题目标题': ['测试单选', '测试多选', '测试判断'],
            '题目内容': ['单选题目内容', '多选题目内容', '判断题目内容'],
            '选项A': ['选项A1', '选项A2', '正确'],
            '选项B': ['选项B1', '选项B2', '错误'],
            '选项C': ['选项C1', '选项C2', ''],
            '选项D': ['选项D1', '选项D2', ''],
            '答案': ['A', 'A,B', 'A'],
            '难度': ['简单', '中等', '困难'],
            '分值': [2.0, 3.0, 1.0],
            '科目': ['数学', '数学', '数学']
        }

        df = pd.DataFrame(data)

        # 将DataFrame保存到内存中的Excel文件
        output = io.BytesIO()
        with pd.ExcelWriter(output, engine='openpyxl') as writer:
            df.to_excel(writer, index=False)
        output.seek(0)

        # 创建上传文件对象
        uploaded_file = SimpleUploadedFile(
            'test_questions.xlsx',
            output.getvalue(),
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )

        # 导入题目
        importer = QuestionImporter(uploaded_file, self.question_bank, self.user)
        result = importer.import_questions()

        # 验证结果
        self.assertEqual(result['total_rows'], 3)
        self.assertEqual(result['imported'], 3)
        self.assertEqual(result['skipped'], 0)
        self.assertEqual(len(result['errors']), 0)

        # 验证题目已创建
        self.assertEqual(Question.objects.count(), 3)
        self.assertEqual(QuestionBankItem.objects.count(), 3)

        # 更新题库统计信息
        self.question_bank.refresh_from_db()
        self.assertEqual(self.question_bank.question_count, 3)


class QuestionValidatorTests(TestCase):
    """题目验证器测试"""

    def test_validate_valid_question(self):
        """测试验证有效题目"""
        data = {
            'title': '有效题目',
            'stem': '这是一个有效的题目',
            'type': 'choice',
            'options': ['A', 'B', 'C', 'D'],
            'answer': 'A',
            'difficulty': 3
        }

        errors = QuestionValidator.validate_question_data(data)
        self.assertEqual(len(errors), 0)

    def test_validate_missing_required_fields(self):
        """测试验证缺少必填字段"""
        data = {
            'title': '',  # 空标题
            'stem': '测试内容',
            'type': 'choice',
            'answer': 'A'
        }

        errors = QuestionValidator.validate_question_data(data)
        self.assertGreater(len(errors), 0)
        self.assertTrue(any('title' in error for error in errors))

    def test_validate_invalid_question_type(self):
        """测试验证无效题目类型"""
        data = {
            'title': '测试题目',
            'stem': '测试内容',
            'type': 'invalid_type',
            'answer': 'A'
        }

        errors = QuestionValidator.validate_question_data(data)
        self.assertGreater(len(errors), 0)
        self.assertTrue(any('题目类型' in error for error in errors))

    def test_validate_insufficient_options(self):
        """测试验证选项不足"""
        data = {
            'title': '测试题目',
            'stem': '测试内容',
            'type': 'choice',
            'options': ['A'],  # 只有一个选项
            'answer': 'A'
        }

        errors = QuestionValidator.validate_question_data(data)
        self.assertGreater(len(errors), 0)
        self.assertTrue(any('至少需要2个选项' in error for error in errors))

    def test_validate_invalid_difficulty(self):
        """测试验证无效难度"""
        data = {
            'title': '测试题目',
            'stem': '测试内容',
            'type': 'choice',
            'options': ['A', 'B'],
            'answer': 'A',
            'difficulty': 10  # 超出范围
        }

        errors = QuestionValidator.validate_question_data(data)
        self.assertGreater(len(errors), 0)
        self.assertTrue(any('难度必须在1-5之间' in error for error in errors))


class QuestionExporterTests(TestCase):
    """题目导出器测试"""

    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123',
            role='teacher'
        )

        # 创建测试题目
        self.question1 = Question.objects.create(
            title='导出测试题1',
            stem='测试导出功能',
            type='choice',
            options=['A', 'B', 'C', 'D'],
            answer='A',
            creator=self.user,
            subject='数学',
            analysis='这是解析',
            explanation='这是解释'
        )

        self.question2 = Question.objects.create(
            title='导出测试题2',
            stem='测试导出功能2',
            type='true_false',
            options=['正确', '错误'],
            answer='A',
            creator=self.user,
            subject='物理'
        )

    def test_export_to_excel(self):
        """测试导出为Excel"""
        questions = Question.objects.filter(id__in=[self.question1.id, self.question2.id])
        exporter = QuestionExporter(questions, 'xlsx', True, True)

        file_data = exporter.export()

        self.assertIsInstance(file_data, bytes)
        self.assertGreater(len(file_data), 0)

    def test_export_to_json(self):
        """测试导出为JSON"""
        questions = Question.objects.filter(id__in=[self.question1.id])
        exporter = QuestionExporter(questions, 'json', True, True)

        file_data = exporter.export()

        self.assertIsInstance(file_data, bytes)

        # 解析JSON验证内容
        json_data = json.loads(file_data.decode('utf-8'))
        self.assertEqual(len(json_data), 1)
        self.assertEqual(json_data[0]['title'], '导出测试题1')
        self.assertIsNotNone(json_data[0]['answer'])  # 包含答案
        self.assertIsNotNone(json_data[0]['analysis'])  # 包含解析

    def test_export_without_answers(self):
        """测试不包含答案的导出"""
        questions = Question.objects.filter(id__in=[self.question1.id])
        exporter = QuestionExporter(questions, 'json', False, False)

        file_data = exporter.export()
        json_data = json.loads(file_data.decode('utf-8'))

        self.assertIsNone(json_data[0]['answer'])  # 不包含答案
        self.assertIsNone(json_data[0]['analysis'])  # 不包含解析